import sys
import uuid
import parse
import camera
import client
import misc
import avatar
from pyglet import gl
from pyglet.graphics import Batch
import euclid
import logging
import config
import math

_context = None

class Context:
    """
    Serve as a base for client using a 3D view (visitor and guide)
    """
    def __init__(self):
        self.avatars = {}
        self._loaded = False

        self._batch = Batch()
        self.cam_settings = parse.CamSettings()
        
    def init_network(self):
        self.client_base = client.ClientBase(self)
        self.client_base.init_network("visitor")
        
    def load_scene(self, start_loc):
        self.scene = parse.parser(misc.data_path("scene.xml"))
        self.cam_settings.position = start_loc
        self.camera = camera.Camera()
        
        self._loaded = True
        
    def get_position(self):
        return self.camera._position.xyz
    
    def get_rot(self):
        return self.camera._rot_y
    
    def get_speed(self):
        return self.camera._mov.xyz
        
    def draw(self):
        if not self._loaded:
            self.client_base.poll()
            return
        
        gl.glMatrixMode(gl.GL_MODELVIEW)
        gl.glLoadIdentity()
        
        self.camera.lookAt()
        
        gl.glLightfv(gl.GL_LIGHT0, gl.GL_POSITION, misc.vec(0.0, 10.0, 5.0, 1.0))
        gl.glLightfv(gl.GL_LIGHT0, gl.GL_DIFFUSE, misc.vec(1.0, 1.0, 1.0, 1.0))
        
        self._batch.draw()

    def update_draw(self, dt):
        if not self._loaded:
            self.client_base.poll()
            return
        
        self.camera.update(dt)
        
        for avatar in self.avatars.values():
            avatar.update(dt)
            
    def update_network(self, dt):
        if self._loaded:
            block_message = {"cmd" : "client_info",
                             "id" : self.client_id,
                             "pos" : client.client_to_serv(self.get_position())}
               
            
            aura_message = {"cmd" : "client_info",
                            "type" : "visitor",
                            "id" : self.client_id,
                            "pos" : self.get_position(),
                            "speed" : self.get_speed(),
                            "rot" : self.get_rot()}
            
            self.client_base.send_update(block_message, aura_message)
        self.client_base.poll()
        
    def client_hello(self, message):
        client_id = message["id"]
        if message["type"] == "visitor":
            logging.info("New avatar %s", client_id)
            new_avatar = avatar.Avatar(message["pos"])
        elif message["type"] == "picture":
            logging.info("New picture %s", client_id)
            new_avatar = avatar.Picture(message["file"], message["pos"], message["rot"],
                                    message["height"], message["width"])
        else:
            logging.warning("Unknown avatar type received : %s", message["type"])
            return

        self.avatars[client_id] = new_avatar
    
    def client_info(self, message):
        client_id = message["id"]
        try:
            avatar = self.avatars[client_id]
        except KeyError:
            logging.warning("Received update for non existing avatar %s", client_id)
            return

        if message["type"] == "visitor":
            avatar.network_update(message["pos"], message["speed"], message["rot"])
    
    def client_bye(self, client_id):
        #sometimes we may receive two bye notifications
        #because we have two connections with other clients
        try:
            self.avatars[client_id].delete()
            del self.avatars[client_id]
        except KeyError:
            logging.warning("Received bye for non existing avatar %s", client_id)
            return
        
    def index_hello_hook(self, message):
        message["type"] = "visitor"

    def block_hello_hook(self, message):
        message["pos"] = client.client_to_serv(self.get_position())
    
    def aura_hello_hook(self, message):
        message["type"] = "visitor"
        message["pos"] = self.get_position()
        
    def inside_focus(self, position):
        vec1 = euclid.Vector3(*self.get_position())
        vec2 = euclid.Vector3(*position)
        
        if (vec1 - vec2).magnitude_squared() < config.focus_size ** 2:
            return math.fabs((vec2 - vec1).angle(self.camera._target * euclid.Vector3(1., 0., 1.))) < config.focus_angle / 2.
        else:
            return False

_context = Context()